home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / aminet / util / gnu / textutl3.lha / textutils-1.3 / src / comm.c < prev    next >
C/C++ Source or Header  |  1992-06-29  |  5KB  |  222 lines

  1. /* comm -- compare two sorted files line by line.
  2.    Copyright (C) 1986, 1990, 1991 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Written by Richard Stallman and David MacKenzie. */
  19.  
  20. #include <stdio.h>
  21. #include <getopt.h>
  22. #include <sys/types.h>
  23. #include "system.h"
  24. #include "linebuffer.h"
  25.  
  26. #define min(x, y) ((x) < (y) ? (x) : (y))
  27.  
  28. /* If nonzero, print lines that are found only in file 1. */
  29. int only_file_1;
  30.  
  31. /* If nonzero, print lines that are found only in file 2. */
  32. int only_file_2;
  33.  
  34. /* If nonzero, print lines that are found in both files. */
  35. int both;
  36.  
  37. /* The name this program was run with. */
  38. char *program_name;
  39.  
  40. int compare_files ();
  41. void error ();
  42. void writeline ();
  43. void usage ();
  44.  
  45. void
  46. main (argc, argv)
  47.      int argc;
  48.      char *argv[];
  49. {
  50.   int c;
  51.  
  52.   program_name = argv[0];
  53.  
  54.   only_file_1 = 1;
  55.   only_file_2 = 1;
  56.   both = 1;
  57.  
  58.   while ((c = getopt (argc, argv, "123")) != EOF)
  59.     switch (c)
  60.       {
  61.       case '1':
  62.     only_file_1 = 0;
  63.     break;
  64.  
  65.       case '2':
  66.     only_file_2 = 0;
  67.     break;
  68.  
  69.       case '3':
  70.     both = 0;
  71.     break;
  72.  
  73.       default:
  74.     usage ();
  75.       }
  76.  
  77.   if (optind + 2 != argc)
  78.     usage ();
  79.  
  80.   exit (compare_files (argv + optind));
  81. }
  82.  
  83. /* Compare INFILES[0] and INFILES[1].
  84.    If either is "-", use the standard input for that file.
  85.    Assume that each input file is sorted;
  86.    merge them and output the result.
  87.    Return 0 if successful, 1 if any errors occur. */
  88.  
  89. int
  90. compare_files (infiles)
  91.      char **infiles;
  92. {
  93.   /* For each file, we have one linebuffer in lb1.  */
  94.   struct linebuffer lb1[2];
  95.  
  96.   /* thisline[i] points to the linebuffer holding the next available line
  97.      in file i, or is NULL if there are no lines left in that file.  */
  98.   struct linebuffer *thisline[2];
  99.  
  100.   /* streams[i] holds the input stream for file i.  */
  101.   FILE *streams[2];
  102.  
  103.   int i, ret = 0;
  104.  
  105.   /* Initialize the storage. */
  106.   for (i = 0; i < 2; i++)
  107.     {
  108.       initbuffer (&lb1[i]);
  109.       thisline[i] = &lb1[i];
  110.       streams[i] = strcmp (infiles[i], "-")
  111.     ? fopen (infiles[i], "r") : stdin;
  112.       if (!streams[i])
  113.     {
  114.       error (0, errno, "%s", infiles[i]);
  115.       return 1;
  116.     }
  117.  
  118.       thisline[i] = readline (thisline[i], streams[i]);
  119.     }
  120.  
  121.   while (thisline[0] || thisline[1])
  122.     {
  123.       int order;
  124.  
  125.       /* Compare the next available lines of the two files.  */
  126.  
  127.       if (!thisline[0])
  128.     order = 1;
  129.       else if (!thisline[1])
  130.     order = -1;
  131.       else
  132.     {
  133.       /* Cannot use bcmp -- it only returns a boolean value. */
  134.       order = memcmp (thisline[0]->buffer, thisline[1]->buffer,
  135.               min (thisline[0]->length, thisline[1]->length));
  136.       if (order == 0)
  137.         order = thisline[0]->length - thisline[1]->length;
  138.     }
  139.  
  140.       /* Output the line that is lesser. */
  141.       if (order == 0)
  142.     writeline (thisline[1], stdout, 3);
  143.       else if (order > 0)
  144.     writeline (thisline[1], stdout, 2);
  145.       else
  146.     writeline (thisline[0], stdout, 1);
  147.  
  148.       /* Step the file the line came from.
  149.      If the files match, step both files.  */
  150.       if (order >= 0)
  151.     thisline[1] = readline (thisline[1], streams[1]);
  152.       if (order <= 0)
  153.     thisline[0] = readline (thisline[0], streams[0]);
  154.     }
  155.  
  156.   /* Free all storage and close all input streams. */
  157.   for (i = 0; i < 2; i++)
  158.     {
  159.       free (lb1[i].buffer);
  160.       if (ferror (streams[i]) || fclose (streams[i]) == EOF)
  161.     {
  162.       error (0, errno, "%s", infiles[i]);
  163.       ret = 1;
  164.     }
  165.     }
  166.   if (ferror (stdout) || fclose (stdout) == EOF)
  167.     {
  168.       error (0, errno, "write error");
  169.       ret = 1;
  170.     }
  171.   return ret;
  172. }
  173.  
  174. /* Output the line in linebuffer LINE to stream STREAM
  175.    provided the switches say it should be output.
  176.    CLASS is 1 for a line found only in file 1,
  177.    2 for a line only in file 2, 3 for a line in both. */
  178.  
  179. void
  180. writeline (line, stream, class)
  181.      struct linebuffer *line;
  182.      FILE *stream;
  183.      int class;
  184. {
  185.   switch (class)
  186.     {
  187.     case 1:
  188.       if (!only_file_1)
  189.     return;
  190.       break;
  191.  
  192.     case 2:
  193.       if (!only_file_2)
  194.     return;
  195.       /* Skip the tab stop for case 1, if we are printing case 1.  */
  196.       if (only_file_1)
  197.     putc ('\t', stream);
  198.       break;
  199.  
  200.     case 3:
  201.       if (!both)
  202.     return;
  203.       /* Skip the tab stop for case 1, if we are printing case 1.  */
  204.       if (only_file_1)
  205.     putc ('\t', stream);
  206.       /* Skip the tab stop for case 2, if we are printing case 2.  */
  207.       if (only_file_2)
  208.     putc ('\t', stream);
  209.       break;
  210.     }
  211.  
  212.   fwrite (line->buffer, sizeof (char), line->length, stream);
  213.   putc ('\n', stream);
  214. }
  215.  
  216. void
  217. usage ()
  218. {
  219.   fprintf (stderr, "Usage: %s [-123] file1 file2\n", program_name);
  220.   exit (1);
  221. }
  222.